home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / utils1 / iochek.arj / IOCHEK.PAS < prev   
Pascal/Delphi Source File  |  1993-12-13  |  69KB  |  1,736 lines

  1. unit IOChek;
  2.  
  3. { Posted on CompuServe by Mark Reichert, 72763,2417 }
  4.  
  5. { This is a unit containing many functions for handling I/O operations and
  6.   returning the error code in the function return, though any that encapsulate
  7.   BP functions will return that information in the function return and the error
  8.   code in a VAR parameter, i.e.  the EOF function here is still Boolean and
  9.   returns the error as a parameter.   As I explain below, I made this unit
  10.   because when I was in a hurry, I improperly handled error conditions with the
  11.   GetFAttr procedure.  All the work with Enums and Strings is to ease detecting
  12.   what error occured and generating a message.  I even use the Enums in some
  13.   of the functions below in case statements as they are intended to be used.
  14.  
  15.   This unit is provide AS IS.  I DO NOT guarantee that there are no defects,
  16.   though I have been over the code several times.   I am providing this for
  17.   free.  I only ask that anybody that does find a defect please send me an
  18.   e-mail pointing it out so that I can slowly refine the unit.  It is really
  19.   my hope that with this as an example, one of the Pascal third party support
  20.   houses or even Borland itself will provide a unit that eases I/O operations
  21.   for people who cannot do to previous work use the event driven, windowed
  22.   interfaces of Turbo Vision or ObjectWindows.   I do not have all of the
  23.   obscure Microsoft documentation or the experience that would allow me to
  24.   interpret the error codes correctly and do refined, robust effort.  One
  25.   programmer pointed out, in a message reply to my previous plea for a good
  26.   I/O unit, that in network environment, a file read could return a file lock
  27.   error.  In that case, I would think you could write a procedure that would
  28.   go into a loop to keep checking until the file is free or a parameter set
  29.   timeout limit is reached.  And, yes, I know all the encapsulation adds
  30.   overhead, but its insignificant compared to the file I/O itself.  Besides,
  31.   as machines get faster, more cycles can be used to make the programs more
  32.   reliable, which will be necessary as people with progressively less
  33.   experience with computers start using them.
  34.  
  35.   Any way, I'd like to thank Neil Rubenking for his permission for using the
  36.   DosFlush procedure and Jeffrey Watson, a former , who wrote the
  37.   original code on which a number of the functions are based.
  38.  
  39.   Thanks in advance for any replies.
  40.  
  41.   Mark Reichert, 72763,2417}
  42.  
  43. Interface
  44.  
  45. Uses Dos, Objects ;  { Objects used for the Abstract function }
  46.  
  47. Type Strg12 = String[12];
  48.      Strg40 = String[40];
  49.      ByteSeg    = array [1..65535] of byte ;
  50.      PByteSeg   = ^ByteSeg ;
  51.  
  52.      FileTypeEnum = (TextFile, Typed, UnTyped);
  53.  
  54.      OpenTextEnum = (ResetFile, RewriteFile, AppendFile);
  55.      OpenFileEnum = ResetFile .. RewriteFile;
  56.  
  57. { Error codes only go to 181 due to something I observed in MAIN.ASM in the
  58.   Run-Time Library SYS directory.  The SYS directory contains all the code
  59.   that ends up in the SYSTEM special unit that sits in the TURBO.TPL file,
  60.   is loaded into memory when loading the IDE or command-line compiler, and
  61.   forms the core code of every Pascal program.  Main contains the very first
  62.   code your programs execute, including the loading of special interrupt
  63.   functions over any previously installed by DOS or a TSR.  One of those,
  64.   you would see is the Int 24 Critical Error Handler which as the QUE DOS
  65.   Programmers Reference, 3rd edition defines as "the routine that receives
  66.   control when a critical error is detected.  A critical error generally
  67.   represents a hardware failure of some sort and is usually the aftermath
  68.   of a failed device driver call within DOS."  That same reference shows that
  69.   the error code returned in the DI register for this routine correspond to
  70.   those identified as DOS 3.0 errors below, that is 150 and above.  The first
  71.   thing BP's Int 24 does is AND DI, 01FH which means only the lower 5 bits
  72.   are retained and then ADD DI, 150, so as even the comment in MAIN.ASM
  73.   declares the error code in DI has been translated to the range 150..181.
  74.  
  75.   However, there is a better way.  The Int 21, Function 59H (Get Extended
  76.   Error Information), which was first available in DOS 3.0, is safe to call
  77.   from an Int 24 handler, as is Function 30H (Get Dos Version).  So, as is
  78.   done in ParamStr to allow ParamStr(0) = Program Path and FileName, the
  79.   Int 24 handler could check the DosVersion and if Dos 3+ is running, make
  80.   the call to 59H and place the return values in special system variables,
  81.   otherwise zero those variables out and do things the old way.  It would
  82.   also be necessary to have I/O checking code do this.  This way more
  83.   information about the error would be available to programs running on top
  84.   of Dos 3 or greater.  After all, any program that depends on getting a
  85.   non-null string from ParamStr(0) in order to force itself to be run
  86.   from its directory, as one of mine does, has to be run on Dos 3 or better.
  87.   I'm also using Function 5A (Create Uniquely Named File) for a swap file,
  88.   and that function needs Dos 3+ as well. }
  89.  
  90.  
  91.      ErrorEnum = ( NoError,                { 0 }
  92.                    InvalidFunc,            { 1 }
  93.                    FileNotFound,           { 2 }
  94.                    PathNotFound,           { 3 }
  95.                    NoHandlesAvail,         { 4 }
  96.                    AccessDenied,           { 5 }
  97.                    InvalidHandle,          { 6 }
  98.                    MCBDestroyed,           { 7 }
  99.                    InsufficientMemory,     { 8 }
  100.                    InvalidMemBlock,        { 9 }
  101.                    InvalidEnviron,        { 10 }
  102.                    InvalidFormat,         { 11 }
  103.                    InvalidAccess,         { 12 }
  104.                    InvalidData,           { 13 }
  105.                    Reserved0,             { 14 }
  106.                    InvalidDrive,          { 15 }
  107.                    AttemptRemCurrDir,     { 16 }
  108.                    NotSameDevice,         { 17 }
  109.                    NoMoreFiles,           { 18 }
  110. Dummy19,
  111. Dummy20, Dummy21, Dummy22, Dummy23, Dummy24, Dummy25, Dummy26, Dummy27, Dummy28, Dummy29,
  112. Dummy30, Dummy31, Dummy32, Dummy33, Dummy34, Dummy35, Dummy36, Dummy37, Dummy38, Dummy39,
  113. Dummy40, Dummy41, Dummy42, Dummy43, Dummy44, Dummy45, Dummy46, Dummy47, Dummy48, Dummy49,
  114. Dummy50, Dummy51, Dummy52, Dummy53, Dummy54, Dummy55, Dummy56, Dummy57, Dummy58, Dummy59,
  115. Dummy60, Dummy61, Dummy62, Dummy63, Dummy64, Dummy65, Dummy66, Dummy67, Dummy68, Dummy69,
  116. Dummy70, Dummy71, Dummy72, Dummy73, Dummy74, Dummy75, Dummy76, Dummy77, Dummy78, Dummy79,
  117. Dummy80, Dummy81, Dummy82, Dummy83, Dummy84, Dummy85, Dummy86, Dummy87, Dummy88, Dummy89,
  118. Dummy90, Dummy91, Dummy92, Dummy93, Dummy94, Dummy95, Dummy96, Dummy97, Dummy98, Dummy99,
  119.  
  120.                    DiskReadError,        { 100 }
  121.                    DiskWriteError,       { 101 }
  122.                    FileNotAssigned,      { 102 }
  123.                    FileNotOpen,          { 103 }
  124.                    FileNotOpenForInput,  { 104 }
  125.                    FileNotOpenForOutput, { 105 }
  126.                    InvalidNumericFormat, { 106 }
  127.  
  128. Dummy107, Dummy108, Dummy109,
  129. Dummy110, Dummy111, Dummy112, Dummy113, Dummy114, Dummy115, Dummy116, Dummy117, Dummy118, Dummy119,
  130. Dummy120, Dummy121, Dummy122, Dummy123, Dummy124, Dummy125, Dummy126, Dummy127, Dummy128, Dummy129,
  131. Dummy130, Dummy131, Dummy132, Dummy133, Dummy134, Dummy135, Dummy136, Dummy137, Dummy138, Dummy139,
  132. Dummy140, Dummy141, Dummy142, Dummy143, Dummy144, Dummy145, Dummy146, Dummy147, Dummy148, Dummy149,
  133.  
  134.                    DiskWriteProtect,     { 150 }
  135.                    UnknownUnit,          { 151 }
  136.                    DriveNotReady,        { 152 }
  137.                    UnknownCommand,       { 153 }
  138.                    CRCErrorinData,       { 154 }
  139.                    BadReqStructLeng,     { 155 }
  140.                    DiskSeekError,        { 156 }
  141.                    UnknownMediaType,     { 157 }
  142.                    SectorNotFound,       { 158 }
  143.                    OutOfPaper,           { 159 }
  144.                    DeviceWriteFault,     { 160 }
  145.                    DeviceReadFault,      { 161 }
  146.                    GeneralFailure,       { 162 }
  147.                    SharingViolation,     { 163 }
  148.                    LockViolation,        { 164 }
  149.                    InvalidDiskChange,    { 165 }
  150.                    FCBUnavailable,       { 166 }
  151.                    SharingBufferOverflow,{ 167 }
  152.                    CodePageMismatch,     { 168 }
  153.                    EndofInputFile,       { 169 }
  154.                    DiskFull,             { 170 }
  155.                    Reserved1,            { 171 }
  156.                    Reserved2,            { 172 }
  157.                    Reserved3,            { 173 }
  158.                    Reserved4,            { 174 }
  159.                    Reserved5,            { 175 }
  160.                    Reserved6,            { 176 }
  161.                    Reserved7,            { 177 }
  162.                    Reserved8,            { 178 }
  163.                    Reserved9,            { 179 }
  164.                    Reserved10,           { 180 }
  165.                    UnsupportedNetworkReq, { 181 }
  166.                    UnknownDOSError) ;
  167.  
  168. Const
  169.  
  170.      ErrorStr : Array[ErrorEnum] of Strg40 =
  171.                   { Regular I/O Errors }
  172.                     ( 'No Error',                      { 0 }
  173.                       'Invalid Function',              { 1 }
  174.                       'File Not Found',                { 2 }
  175.                       'Path Not Found',                { 3 }
  176.                       'No Handles Available',          { 4 }
  177.                       'Access Denied',                 { 5 }
  178.                       'Invalid Handle',                { 6 }
  179.                       'Memory Control Block Destroyed',{ 7 }
  180.                       'Insufficent Memory',            { 8 }
  181.                       'Invalid Memory Block',          { 9 }
  182.                       'Invalid Environment',          { 10 }
  183.                       'Invalid Format',               { 11 }
  184.                       'Invalid Access',               { 12 }
  185.                       'Invalid Data',                 { 13 }
  186.                       'Reserved',                     { 14 }
  187.                       'Invalid Drive',                { 15 }
  188.                       'Attempt To Remove Current Directory',{ 16 }
  189.                       'Attempt To Rename Across Drives',    { 17 }
  190.                       'No More Files',     { 18 }
  191. '',
  192. '', '', '', '', '', '', '', '', '', '',
  193. '', '', '', '', '', '', '', '', '', '',
  194. '', '', '', '', '', '', '', '', '', '',
  195. '', '', '', '', '', '', '', '', '', '',
  196. '', '', '', '', '', '', '', '', '', '',
  197. '', '', '', '', '', '', '', '', '', '',
  198. '', '', '', '', '', '', '', '', '', '',
  199. '', '', '', '', '', '', '', '', '', '',
  200.  
  201.                     { Borland Pascal Determined Errors }
  202.                       'Disk Read Error',         { 100 }
  203.                       'Disk Write Error',        { 101 }
  204.                       'File Not Assigned',       { 102 }
  205.                       'File Not Open',           { 103 }
  206.                       'File Not Open For Input', { 104 }
  207.                       'File Not Open For Output',{ 105 }
  208.                       'Invalid Numeric Format', { 106 }
  209.  
  210. '', '', '',
  211. '', '', '', '', '', '', '', '', '', '',
  212. '', '', '', '', '', '', '', '', '', '',
  213. '', '', '', '', '', '', '', '', '', '',
  214. '', '', '', '', '', '', '', '', '', '',
  215.  
  216.                     { Critical Errors }
  217.                       'Disk Write Protected',            { 150 }
  218.                       'Unknown Unit',                    { 151 }
  219.                       'Drive Not Ready',                 { 152 }
  220.                       'Unknown Command',                 { 153 }
  221.                       'CRC Error in Data',               { 154 }
  222.                       'Bad Requested Structure Length',  { 155 }
  223.                       'Disk Seek Error',                 { 156 }
  224.                       'Unknown Media Type',              { 157 }
  225.                       'Sector Not Found',                { 158 }
  226.                       'Out Of Paper',                    { 159 }
  227.                       'Device Write Fault',              { 160 }
  228.                       'Device Read Fault',               { 161 }
  229.                       'General Failure',                 { 162 }
  230.                       'Sharing Violation',               { 163 }
  231.                       'Lock Violation',                  { 164 }
  232.                       'Invalid Disk Change',             { 165 }
  233.                       'File Control Block Unavailable',  { 166 }
  234.                       'Sharing Buffer Overflow',         { 167 }
  235.                       'Code Page Mismatch',              { 168 }
  236.                       'End of Input File',               { 169 }
  237.                       'Disk Full',                       { 170 }
  238.                       'Reserved',                        { 171 }
  239.                       'Reserved',                        { 172 }
  240.                       'Reserved',                        { 173 }
  241.                       'Reserved',                        { 174 }
  242.                       'Reserved',                        { 175 }
  243.                       'Reserved',                        { 176 }
  244.                       'Reserved',                        { 177 }
  245.                       'Reserved',                        { 178 }
  246.                       'Reserved',                        { 179 }
  247.                       'Reserved',                        { 180 }
  248.                       'Unsupported Network Request',   { 181 }
  249.                       'Unknown DOS Error') ;
  250.  
  251.  
  252. Type
  253.  
  254. PFindFileObj = ^TFindFileObj;
  255. TFindFileObj = Object
  256.                  FFError : Integer;
  257.                  FSearch : Dos.SearchRec;
  258.                  FAttr : Byte;
  259.                  FTime : Longint;
  260.                  FYear  : Word;
  261.                  FMonth : Word;
  262.                  FDay   : Word;
  263.                  FHour  : Word;
  264.                  FMin   : Word;
  265.                  FSec   : Word;
  266.                  FSize : Longint;
  267.                  FNameExt : String[12];
  268.                  FName : String[8];
  269.                  FExt  : String[3];
  270.                  Constructor InitAndFindFirst(Const Path : PathStr; Attr: Word);
  271.                  Destructor EndFindFile;
  272.                  Procedure DoFindNext;
  273.                  Procedure ParseFSearch;
  274.                  Function DoFindFileLoop : Integer;
  275.                  Function DoFileOperation : Integer;  Virtual;
  276.                End;
  277.  
  278. PCopyFileObj = ^TCopyFileObj;
  279. TCopyFileObj = Object
  280.                  Err : Integer;
  281.                  BuffSize : Word;
  282.                  CopyBuffer : PByteSeg;
  283.                  SourceFile : File;
  284.                  DestFile : File;
  285.                  SourceFullPath : PathStr;
  286.                  SourcePath : PathStr;
  287.                  SourceName : Strg12;
  288.                  DestFullPath : PathStr;
  289.                  DestPath :  PathStr;
  290.                  DestName : Strg12;
  291.                  Constructor InitCopy(Const Source : PathStr; Const Dest : PathStr);
  292.                  Destructor EndCopy; Virtual;
  293.                  Procedure SetNames(Const FileName : Strg12);
  294.                  Procedure SetPaths(Const SPath : PathStr; Const DPath : PathStr);
  295.                  Function GetErr : Integer;
  296.                  Function OpenFiles : Integer;
  297.                  Function CloseFiles : Integer;
  298.                  Function DoCopies : Integer;
  299.                  Function DoFileCopy : Integer; Virtual;
  300.                End;
  301.  
  302. PMoveFileObj = ^TMoveFileObj;
  303. TMoveFileObj = Object(TCopyFileObj)
  304.                  Constructor InitMove(Source, Dest : PathStr);
  305.                  Destructor EndMove; Virtual;
  306.                  Function DoFileCopy : Integer; Virtual;
  307.                End;
  308.  
  309.   Function IO_IsCoveredError(ErrorNo : Integer) : Boolean;
  310.   Function IO_GetErrorEnum(ErrorNo : Integer) : ErrorEnum;
  311.   Function IO_GetErrorStr(ErrorNo : Integer) : Strg40;
  312.   Function IO_GetErrorStrFromEnum(Error : ErrorEnum) : Strg40;
  313.   Function IO_AddSlash(InPath : String) : String;
  314.   Function IO_DelSlash(InPath : String) : String;
  315.   Function IO_EditPathForDos(InPath : String) : String;
  316.   Function IO_DosFlush(Var F) : Word;
  317.   Function IO_ChDir(CONST Path : PathStr) : Integer;
  318.   Function IO_CD(CONST Path : PathStr) : Integer;
  319.   Function IO_GD(Drive : char) : PathStr ;
  320.   Function IO_MkDir(Const Path : PathStr) : Integer ;
  321.   Function IO_MD(Const Path : PathStr) : Integer ;
  322.   Function IO_RmDir(Const Path : PathStr) : Integer ;
  323.   Function IO_RD(Const Path : PathStr) : Integer;
  324.   Function IO_ShareInstalled : Boolean ;
  325.   Function IO_ReadOnly : Byte ;
  326.   Function IO_ReadWrite : Byte ;
  327.   Function IO_WriteOnly : Byte ;
  328.   Function IO_FileMode(InMode : Word) : Byte ;
  329.   Function IO_ResetText(Var TextFile : Text) : Integer;
  330.   Function IO_RewriteText(Var TextFile : Text) : Integer;
  331.   Function IO_AppendText(Var TextFile : Text) : Integer;
  332.   Function IO_OpenText(Const PathName : PathStr;
  333.                          Var TextFile : Text;
  334.                              OpenType : OpenTextEnum) : Integer;
  335.   Function IO_ReadTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
  336.   Function IO_ReadLnTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
  337.   Function IO_WriteTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
  338.   Function IO_WriteLnTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
  339.   Function IO_ResetFile(Var GenericFile : File) : Integer;
  340.   Function IO_RewriteFile(Var GenericFile : File) : Integer;
  341.   Function IO_OpenFile( Const PathName : PathStr;
  342.                           Var GenericFile : File;
  343.                               OpenType : OpenFileEnum) : Integer;
  344.   Function IO_ResetFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
  345.   Function IO_RewriteFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
  346.   Function IO_OpenFileBlock( Const PathName : PathStr;
  347.                                Var GenericFile : File;
  348.                                OpenType : OpenFileEnum;
  349.                                BufSize : Word) : Integer;
  350.   Function IO_ResetFileBlock1(Var GenericFile : File) : Integer;
  351.   Function IO_RewriteFileBlock1(Var GenericFile : File) : Integer;
  352.   Function IO_OpenFileBlock1( Const PathName : PathStr;
  353.                                 Var GenericFile : File;
  354.                                 OpenType : OpenFileEnum) : Integer;
  355.   Function IO_BlockRead( Var GenericFile : File;
  356.                            Var Buffer;
  357.                                Count     : Word;
  358.                            Var BytesRead : Word ) : Integer;
  359.   Function IO_BlockReadIntoHeap( Const PathName : PathStr;
  360.                                    Var BuffPtr : Pointer;
  361.                                    Var FSize : Longint ) : Integer;
  362.   Function IO_BlockWrite( Var GenericFile : File;
  363.                             Var Buffer;
  364.                                 Count : Word;
  365.                             Var Result : Word) : Integer;
  366.   Function IO_BlockWriteFromHeap(Const PathName : PathStr;
  367.                                          BuffPtr : Pointer;
  368.                                          FSize : Word ) : Integer;
  369.   Function IO_Close(Var GenericFile : File) : Integer;
  370.   Function IO_CloseText(Var TextFile : Text) : Integer;
  371.   Function IO_CloseFile(Var GenericFile : File) : Integer;
  372.   Function IO_CloseTextFile(Var TextFile : Text) : Integer;
  373.   Function IO_FlushToDos(Var TextFile : Text) : Integer;
  374.   Function IO_FlushToDisk(Var TextFile : Text) : Integer;
  375.   Function IO_FilePos(Var GenericFile : File; Var FPos : Longint) : Integer;
  376.   Function IO_FileSeek(Var GenericFile : File; FSeek : Longint) : Integer;
  377.   Function IO_GoFileSeek( Const PathName : PathStr;
  378.                             Var GenericFile : File;
  379.                                 FSeek : Longint) : integer ;
  380.   Function IO_EOF(Var GenericFile : File; Var ErrCode : Integer) : Boolean;
  381.   Function IO_EOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  382.   Function IO_SeekEOF(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  383.   Function IO_SeekEOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  384.   Function IO_GetFTime(Var GenericFile : File; Var InTime : Longint) : Integer;
  385.   Function IO_SetFTime(Var GenericFile : File; InTime : Longint) : Integer;
  386.   Function IO_GetFileTime(Const PathName : PathStr; Var Time : Longint) : Integer;
  387.   Function IO_SetFileTime(Const PathName : PathStr ; Time : Longint) : Integer;
  388.   Function IO_GetFAttr(Var GenericFile : File; Var Attr : word) : Integer;
  389.   Function IO_GetFileAttr(Const PathName : PathStr; Var Attr : word) : Integer;
  390.   Function IO_SetFAttr(Var GenericFile : File; Attr : word) : Integer ;
  391.   Function IO_SetFileAttr(Const PathName : PathStr; Attr : word) : Integer;
  392.   Function IO_FileIsHere(FullName : PathStr) : Boolean;
  393.   Function IO_Exists(Path : PathStr ; Attr : byte) : Boolean ;
  394.   Function IO_RenameFile( Const OldName : PathStr;
  395.                             Const NewName : PathStr) : integer ;
  396.   Function IO_Erase(Var GenericFile : File) : Integer;
  397.   Function IO_EraseText(Var TextFile : Text) : Integer;
  398.   Function IO_EraseFile(Const PathName : PathStr) : integer ;
  399.   Function IO_KillAFile(Const PathName : PathStr) : integer ;
  400.   Function IO_EraseFiles(Path,FileSpec : PathStr) : integer ;
  401.   Function IO_Remove(InPath : PathStr) : Integer;
  402.   Function IO_FileSize(Var GenericFile : File; Var Size : Longint) : Integer;
  403.   Function IO_GetNoOfRecords(Const PathName : PathStr; Var Size : Longint; RecordSize : Word) : integer ;
  404.   Function IO_GetFileSize(Const PathName : PathStr; Var FileSize : Longint) : integer ;
  405.   Function IO_Equals(Var X, Y; Index : word; Size : word) : Boolean;
  406.  
  407. Type
  408.    IOErrorValues = set of 0..181;  { Set of valid error codes }
  409.  
  410. Const
  411.    RegIOErrors : IOErrorValues = [ord(NoError)..Ord(NoMoreFiles)];
  412.    BPRTLErrors : IOErrorValues = [ord(DiskReadError)..ord(InvalidNumericFormat)];
  413.    CritErrors  : IOErrorValues = [ord(DiskWriteProtect)..ord(UnsupportedNetworkReq)];
  414.    CoveredErrorNumbers : IOErrorValues = [ ord(NoError)..ord(NoMoreFiles),
  415.                                            ord(DiskReadError)..ord(InvalidNumericFormat),
  416.                                            ord(DiskWriteProtect)..ord(UnsupportedNetworkReq)];
  417.    CopyPtr : PCopyFileObj = Nil;
  418.    MovePtr : PMoveFileObj = Nil;
  419.  
  420. Implementation
  421.  
  422.   Function IO_IsCoveredError(ErrorNo : Integer) : Boolean;
  423.   Begin
  424.     IO_IsCoveredError := ErrorNo in CoveredErrorNumbers;
  425.   End;
  426.  
  427.   Function IO_GetErrorEnum(ErrorNo : Integer) : ErrorEnum;
  428.   Begin
  429.     If IO_IsCoveredError(ErrorNo) Then
  430.       IO_GetErrorEnum := ErrorEnum(ErrorNo)
  431.     Else
  432.       IO_GetErrorEnum := UnknownDOSError;
  433.   End;
  434.  
  435.   Function IO_GetErrorStr(ErrorNo : Integer) : Strg40;
  436.   Begin
  437.     If IO_IsCoveredError(ErrorNo) Then
  438.       IO_GetErrorStr := ErrorStr[ErrorEnum(ErrorNo)]
  439.     Else
  440.       IO_GetErrorStr := ErrorStr[UnknownDOSError];
  441.   End;
  442.  
  443.   Function IO_GetErrorStrFromEnum(Error : ErrorEnum) : Strg40;
  444.   Begin
  445.      IO_GetErrorStrFromEnum := ErrorStr[Error];
  446.   End;
  447.  
  448. {Dos Conversion }
  449.    { Adds BackSlash at end of Path if not one there already }
  450.  
  451.   Function IO_AddSlash(InPath : String) : String; assembler;
  452.   asm
  453.     mov dx, ds
  454.     les di, @Result              { get result destination }
  455.     lds si, InPath               { get address of Instr }
  456.     xor ax, ax
  457.     lodsb                        { load InPath length byte }
  458.     stosb                        { store it in Result length byte }
  459.     mov cx, ax
  460.     jcxz @1                      { skip rest if an empty string }
  461.     rep movsb                    { move Instr to Result }
  462.     cmp byte ptr es:[di-1], '\'  { see if there is a slash at end }
  463.     je @1                        { if there is, skip next step }
  464.     mov byte ptr es:[di], '\'    { put '\' at end }
  465.     les di, @Result              { get result destination }
  466.     inc byte ptr es:[di]         { increase length byte }
  467.   @1:
  468.     mov ds, dx
  469.   end;
  470.  
  471. {Dos Conversion }
  472.    { Deletes BackSlash at end of Path if one is there }
  473.   Function IO_DelSlash(InPath : String) : String; assembler;
  474.   asm
  475.     mov dx, ds
  476.     les di, @Result              { get result destination }
  477.     lds si, InPath               { get address of Instr }
  478.     xor ax, ax
  479.     lodsb                        { load InPath length byte }
  480.     mov bx, ax                   { put length in bx for indexing }
  481.     cmp byte ptr [bx+si-1], '\'  { see if there is a slash at end }
  482.     jne @1                       { if there isn't, skip next step }
  483.     dec ax                       { decrease length }
  484.   @1:
  485.     stosb                        { store it in Result length byte }
  486.     mov cx, ax
  487.     rep movsb                    { move Instr to Result }
  488.     mov ds, dx
  489.   end;
  490.  
  491. {Dos Conversion }
  492.    { This Function replaces a Pascal if statement, which edited the path
  493.      before giving it to the BP procedure, that was in several procedures }
  494.  
  495.    { Deletes BackSlash at end of Path if one is there, except when a
  496.      a backslash is valid like '\' or 'A:\' for the root directory  }
  497.  
  498.   Function IO_EditPathForDos(InPath : String) : String; assembler;
  499.   asm
  500.     mov dx, ds
  501.     les di, @Result              { get result destination }
  502.     lds si, InPath               { get address of Instr }
  503.     xor ax, ax
  504.     lodsb                        { load InPath length byte }
  505.     cmp al, 1
  506.     je @1                        { if Instr is 1 char, need not change }
  507.     mov bx, ax                   { put length in bx for indexing }
  508.     cmp byte ptr [si+bx-1], '\'  { see if there is a slash at the end of the string }
  509.     jne @1                       { if there isn't, skip next step }
  510.     cmp byte ptr [si+bx-2], ':'  { see if it was a valid use like 'A:\'}
  511.     je @1                        { if it was, skip next step }
  512.     dec ax                       { decrease length }
  513.   @1:
  514.     stosb                        { store it in Result length byte }
  515.     mov cx, ax
  516.     rep movsb                    { move Instr to Result }
  517.     mov ds, dx
  518.   end;
  519.  
  520.   { This Function is from Turbo Pascal 6.0 Techniques and Utilities,
  521.     pages 610-11, Copyright @ 1991 Neil Rubenking, who gave his permission
  522.     to use this Function.  However, I added a conditional jump that was
  523.     left out, and moved two lines up in the Function so that they didn't
  524.     have to be duplicated in two areas.  There are two points I should make
  525.     in addition to those in the book: 1) This is a word Function because it
  526.     returns the DOS Error code in the AX register Function return, almost
  527.     automatically and 2) This Function only flushes from the DOS buffers to
  528.     disk. To get BP buffers to the disk, you must first use the BP Flush,
  529.     and then call this Function as I do below in IO_FlushToDisk }
  530.   Function IO_DosFlush(Var F) : Word; assembler;
  531.   asm
  532.     mov ax, 3000h      { get Dos Version }
  533.     int 21h
  534.     { next two lines were duplicated in both flow control channels but
  535.       putting them here upstream of the CMPs accomplishes the same purpose }
  536.     les di, F          { get address of file variable }
  537.     mov bx, ES:[DI]    { File handle is first word }
  538.     cmp al, 3          { Dos < 3? old! }
  539.     jl @old
  540.     cmp ah, 1Eh        { Dos < 3.3? old! }
  541.     jl @old            { this line is not in published version, but is needed }
  542.     mov ah, 68h        { commit file Function }
  543.     int 21h
  544.     jc @BadEnd         { Carry Flag set on error, AX = Error Code, so leave }
  545.     jmp @GoodEnd       { Finished! Function 68h handles all, unlike old below }
  546. @old:
  547.     mov ah, 45h        { duplicate handle Function }
  548.     int 21h
  549.     jc @BadEnd
  550. @ok:                { this label just 'names' the following code }
  551.     mov bx, ax         { put duped handle in BX }
  552.     mov ah, 3Eh        {    and close it }
  553.     int 21h
  554.     jc @BadEnd
  555. @GoodEnd:
  556.     mov ax, 0          { no error, so set return value to 0 }
  557. @BadEnd:
  558.   end;
  559.  
  560.   { Change the directory to a presumably correct format Path and get any error }
  561.   Function IO_ChDir(CONST Path : PathStr) : Integer;
  562.   Begin
  563. {$I-} chdir( Path ) ;
  564. {$I+} IO_ChDir := IOResult ;
  565.   End;
  566.  
  567.   { Jeffrey Watson's original Pascal Edit path If statement has been
  568.     replaced with EditPatForDos, and the call to BP Chdir with IO_ChDir }
  569.  
  570.   { Don't presume that Path is correctly formatted, edit it,
  571.     then change the directory, and get any error }
  572.   Function IO_CD(CONST Path : PathStr) : Integer;
  573.   begin
  574.     IO_CD := IO_ChDir( IO_EditPathForDos(Path) ) ;
  575.   end ;
  576.  
  577.   { Make the directory for a presumably correct format Path and get any error }
  578.   Function IO_MkDir(Const Path : PathStr) : Integer ;
  579.   begin
  580. {$I-} mkdir(Path) ;
  581. {$I+} IO_MkDir := IOResult ;
  582.   end ;
  583.  
  584.   { Jeffrey Watson's original Pascal Edit path If statement has been
  585.     replaced with EditPatForDos, and the call to BP Mkdir with IO_MkDir }
  586.  
  587.   { Don't presume that Path is correctly formatted, edit it,
  588.     then make the directory, and get any error }
  589.   Function IO_MD(Const Path : PathStr) : Integer ;
  590.   begin
  591.     IO_MD := IO_MkDir(IO_EditPathForDos(Path) ) ;
  592.   end ;
  593.  
  594.   { Remove the directory for a presumably correct format Path and get any error }
  595.   Function IO_RmDir(Const Path : PathStr) : Integer;
  596.   Begin
  597. {$I-} rmdir( Path ) ;
  598. {$I+} IO_RmDir := IOResult ;
  599.   End;
  600.  
  601.   { Jeffrey Watson's original Pascal Edit path If statement has been
  602.     replaced with EditPatForDos, and the call to BP Rmdir with IO_RmDir }
  603.  
  604.   { Don't presume that Path is correctly formatted, edit it,
  605.     then remove the directory, and get any error }
  606.   Function IO_RD(Const Path : PathStr) : Integer;
  607.   Begin
  608.     IO_RD := IO_RmDir( IO_EditPathForDos(Path) ) ;
  609.   End;
  610.  
  611.   { Jeffrey Watson's original Pascal If statement for translating Drive have been
  612.     replaced with a case statement, but I've kept the return of a null string on error}
  613.  
  614.   { GD: returns the current directory for a drive,
  615.         specified one of a variety of formats }
  616.  
  617.   Function IO_GD(Drive : char) : PathStr ;
  618.  
  619.   Var DosDrive : byte   ;
  620.       Path     : PathStr ;
  621.  
  622.   begin
  623.  
  624.     Case Drive Of
  625.       'A'..'Z' : DosDrive := ord(Drive) - 64 ;
  626.       'a'..'z' : DosDrive := ord(Drive) - 96 ;
  627.       '0'..'9' : DosDrive := ord(Drive) - 48 ;
  628.       #00..#26 : DosDrive := ord(Drive) ;
  629.       #32      : DosDrive := 0 ;
  630.     End;
  631.  
  632.     { If one didn't check for a valid drive, GetDir would be perfectly content
  633.       to hand back a bogus path }
  634.  
  635.     If DiskSize(DosDrive) <> -1 then  { All Invalid Drives should return -1 }
  636.       Begin
  637.         Getdir(DosDrive, Path);  { GetDir DOES NOT AFFECT IORESULT! }
  638.         IO_GD := Path;
  639.       End
  640.     else
  641.       IO_GD := ''  ;  { This makes it readily apparent that a problem occured }
  642.  
  643.   end ; (* GD *)
  644.  
  645.   { Jeffrey Watson's original Pascal Function written before 6.0 using Intr has been
  646.     translated to BASM because doing so is always smaller, faster and SAFER than
  647.     using Intr.
  648.  
  649.     Borland's approach, while a kludge, is one of very few ways of Intr getting
  650.     around Intel's insistance on hardcoded interrupt numbers.  This makes MsDos
  651.     much worse because while making it share code with Intr may save space,
  652.     setting up separate code for putting Reg values into registers and doing a
  653.     Int 21 would have been much safer. }
  654.  
  655.   { Finding out if SHARE.EXE has been installed which would mean additional flags
  656.     must be set to get the proper filemode }
  657.  
  658.   Function IO_ShareInstalled : Boolean ; assembler;
  659.   asm
  660.     mov dx, ds       { save BP Data Segment }
  661.     xor bl, bl       { setup for return }
  662.     mov AH, $30      { Using Int 21, Function 30H, Get Dos Version }
  663.     int 21h
  664.     cmp al, 3        { If not Dos 3+, jump to end, return False }
  665.     jb @1
  666.     xor ax, ax
  667.     mov AH, 10h      { Using Int 2F, Function 10H, Get SHARE.EXE Installation Status }
  668.     int 2Fh
  669.     jc @1            { Carry flag set on error, jump to end, return False }
  670.     cmp Al, 0FFh     { if interrupt returns $FF, SHARE.EXE is installed }
  671.     jne @1           { if not, return FALSE }
  672.     mov bl, 1        { else return TRUE }
  673. @1: mov al, bl       { put in actual return register }
  674.     mov ds, dx
  675.   end;
  676.  
  677.   { ReadOnly: returns readonly status flag }
  678.  
  679.   Function IO_ReadOnly : Byte;
  680.   begin
  681.     if IO_ShareInstalled then
  682.       IO_ReadOnly := $20
  683.     else
  684.       IO_ReadOnly := $00 ;
  685.   end ; { ReadOnly }
  686.  
  687. (*.PA*)
  688.  
  689.   { ReadWrite: returns ReadWrite status flag }
  690.  
  691.   Function IO_ReadWrite : Byte;
  692.   begin
  693.     if IO_ShareInstalled then
  694.       IO_ReadWrite := $42
  695.     else
  696.       IO_ReadWrite := $02 ;
  697.   end ; { ReadWrite }
  698.  
  699.   { WriteOnly: returns writeonly status flag }
  700.   Function IO_WriteOnly : Byte;
  701.   begin
  702.     if IO_ShareInstalled then
  703.       IO_WriteOnly := $31
  704.     else
  705.       IO_WriteOnly := $01 ;
  706.   end ; { WriteOnly }
  707.  
  708.   { Returns the proper Status flag, allows one to use the File mode constants as
  709.     mnumonics for setting the FileMode variable.  I was using it in quite a few
  710.     Functions below until I found that though DOS may pay attention to the file
  711.     mode when opening a file, BP code expects Open Typed and UnTyped files to be
  712.     fmInOut, Text files being read to be fmInput, and Text file being written to
  713.     be fmOutput regardless of the state of the FileMode variable. }
  714.   Function IO_FileMode(InMode : Word) : Byte;
  715.   Begin
  716.     If InMode >= fmInput Then  { If InMode = [fmInput, fmOutput, fmInOut], then reset }
  717.       Dec(InMode, fmInput);
  718.     If InMode > 2 Then
  719.       InMode := 2;
  720.     Case InMode Of
  721.       0 : InMode := IO_ReadOnly;
  722.       1 : InMode := IO_WriteOnly;
  723.       2 : InMode := IO_ReadWrite;
  724.     End;
  725.     IO_FileMode := InMode;
  726.   End;
  727.  
  728.   { The following Open.. Functions largely follow the form of Open Methods that are
  729.     used in objects written by Jeffrey Watson }
  730.  
  731.   { Reset the text file and get the error }
  732.   Function IO_ResetText(Var TextFile : Text) : Integer;
  733.   Begin
  734. {$I-} Reset(TextFile);
  735. {$I+} IO_ResetText := IOResult;
  736.   End;
  737.  
  738.   { Rewrite the text file and get the error }
  739.   Function IO_RewriteText(Var TextFile : Text) : Integer;
  740.   Begin
  741. {$I-} Rewrite(TextFile);
  742. {$I+} IO_RewriteText := IOResult;
  743.   End;
  744.  
  745.   { Append to the text file and get the error }
  746.   Function IO_AppendText(Var TextFile : Text) : Integer;
  747.   Begin
  748. {$I-} Append(TextFile);
  749. {$I+} IO_AppendText := IOResult;
  750.   End;
  751.  
  752.   { Open the text file called PathName according the OpenType and get the error }
  753.  
  754.   Function IO_OpenText(Const PathName : PathStr;
  755.                          Var TextFile : Text;
  756.                          OpenType : OpenTextEnum) : Integer;
  757.   Begin
  758.     Assign(TextFile, PathName);
  759.  
  760.     Case OpenType Of
  761.       ResetFile   : IO_OpenText := IO_ResetText(TextFile);   {Mode = fmInput}
  762.       RewriteFile : IO_OpenText := IO_RewriteText(TextFile); {Mode = fmOutput}
  763.       AppendFile  : IO_OpenText := IO_AppendText(TextFile);  {Mode = fmOutput}
  764.     End;
  765.   End;
  766.  
  767.   { Read a string from the text file and get the error }
  768.  
  769.   Function IO_ReadTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
  770.   Begin
  771. {$I-} Read(TextFile, TextStr); {$I+}
  772.     IO_ReadTextStr := IOResult;
  773.   End;
  774.  
  775.   { Read a line from the text file and get the error }
  776.  
  777.   Function IO_ReadLnTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
  778.   Begin
  779. {$I-} Readln(TextFile, TextStr); {$I+}
  780.     IO_ReadLnTextStr := IOResult;
  781.   End;
  782.  
  783.   { Write a string to the text file and get the error }
  784.   Function IO_WriteTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
  785.   Begin
  786. {$I-} Write(TextFile, TextStr); {$I+}
  787.     IO_WriteTextStr := IOResult;
  788.   End;
  789.  
  790.   { Write a line to the text file and get the error }
  791.   Function IO_WriteLnTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
  792.   Begin
  793. {$I-} Writeln(TextFile, TextStr); {$I+}
  794.     IO_WritelnTextStr := IOResult;
  795.   End;
  796.  
  797.   { Reset the untyped file and get the error }
  798.   Function IO_ResetFile(Var GenericFile : File) : Integer;
  799.   Begin
  800. {$I-} Reset(GenericFile);
  801. {$I+} IO_ResetFile := IOResult;
  802.   End;
  803.  
  804.   { Rewrite the untyped file and get the error }
  805.   Function IO_RewriteFile(Var GenericFile : File) : Integer;
  806.   Begin
  807. {$I-} Rewrite(GenericFile);
  808. {$I+} IO_RewriteFile := IOResult;
  809.   End;
  810.  
  811.   { Open the untyped file called PathName according the OpenType and get the error }
  812.  
  813.   Function IO_OpenFile( Const PathName : PathStr;
  814.                           Var GenericFile : File;
  815.                           OpenType : OpenFileEnum) : Integer;
  816.   Begin
  817.     Assign(GenericFile, PathName);
  818.     Case OpenType Of
  819.       ResetFile   : IO_OpenFile := IO_ResetFile(GenericFile);   {Mode = fmInOut}
  820.       RewriteFile : IO_OpenFile := IO_RewriteFile(GenericFile); {Mode = fmInOut}
  821.     End;
  822.   End;
  823.  
  824.   { These next 3 Functions could be the only way of using this unit on typed files.
  825.     The compiler won't let you work with typed files directly through File, but if you
  826.     make BufSize = SizeOf(Record) then you can approximate the behavior.  Personally,
  827.     though, I think any recurringly used typed file should have an associated object
  828.     written to read and write from/to it, and that object can deal with I/O errors. }
  829.  
  830.   { Reset the untyped file with a set buffer size and get the error }
  831.   Function IO_ResetFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
  832.   Begin
  833. {$I-} Reset(GenericFile, BufSize);
  834. {$I+} IO_ResetFileBlock := IOResult;
  835.   End;
  836.  
  837.   { Rewrite the untyped file with a set buffer size and get the error }
  838.   Function IO_RewriteFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
  839.   Begin
  840. {$I-} Rewrite(GenericFile, BufSize);
  841. {$I+} IO_RewriteFileBlock := IOResult;
  842.   End;
  843.  
  844.   { Open the untyped file called PathName according the OpenType, change the file
  845.     buffer size to the given value, and get the error }
  846.  
  847.   Function IO_OpenFileBlock( Const PathName : PathStr;
  848.                                Var GenericFile : File;
  849.                                OpenType : OpenFileEnum;
  850.                                BufSize : Word) : Integer;
  851.   Begin
  852.     Assign(GenericFile, PathName);
  853.  
  854.     {Mode = fmInOut}
  855.     Case OpenType Of
  856.       ResetFile   : IO_OpenFileBlock := IO_ResetFileBlock(GenericFile, BufSize);
  857.       RewriteFile : IO_OpenFileBlock := IO_RewriteFileBlock(GenericFile, BufSize);
  858.     End;
  859.   End;
  860.  
  861.   { The ones to use with BlockRead/Write on generic files }
  862.   { Reset the untyped file with a buffer = 1 and get the error }
  863.   Function IO_ResetFileBlock1(Var GenericFile : File) : Integer;
  864.   Begin
  865. {$I-} Reset(GenericFile, 1);
  866. {$I+} IO_ResetFileBlock1 := IOResult;
  867.   End;
  868.  
  869.   { Rewrite the untyped file with a buffer = 1 and get the error }
  870.   Function IO_RewriteFileBlock1(Var GenericFile : File) : Integer;
  871.   Begin
  872. {$I-} Rewrite(GenericFile, 1);
  873. {$I+} IO_RewriteFileBlock1 := IOResult;
  874.   End;
  875.  
  876.   { Open the untyped file called PathName according the OpenType, change the file
  877.     buffer size to 1, and get the error }
  878.   Function IO_OpenFileBlock1( Const PathName : PathStr;
  879.                                 Var GenericFile : File;
  880.                                 OpenType : OpenFileEnum) : Integer;
  881.   Begin
  882.     Assign(GenericFile, PathName);
  883.  
  884.     { Mode = fmInOut }
  885.     Case OpenType Of
  886.       ResetFile   : IO_OpenFileBlock1 := IO_ResetFileBlock1(GenericFile);
  887.       RewriteFile : IO_OpenFileBlock1 := IO_RewriteFileBlock1(GenericFile);
  888.     End;
  889.   End;
  890.  
  891.   { Close the GenericFile and get the error }
  892.   Function IO_Close(Var GenericFile : File) : Integer;
  893.   Begin
  894. {$I-} Close(GenericFile);
  895. {$I+} IO_Close := IOResult;
  896.   End;
  897.  
  898.   { Close the TextFile and get the error }
  899.   Function IO_CloseText(Var TextFile : Text) : Integer;
  900.   Begin
  901. {$I-} Close(TextFile);
  902. {$I+} IO_CloseText := IOResult;
  903.   End;
  904.  
  905.   { Close the GenericFile and get the error, but ignore if it was already closed }
  906.   Function IO_CloseFile(Var GenericFile : File) : Integer;
  907.   Var ErrCode : Integer;
  908.   Begin
  909.      ErrCode := IO_Close(GenericFile);
  910.      If ErrCode = Ord(FileNotOpen) Then  { Really, if it was already closed why bother about it }
  911.        IO_CloseFile := 0
  912.      Else
  913.        IO_CloseFile := ErrCode;
  914.   End;
  915.  
  916.   { Close the TextFile and get the error, but ignore if it was already closed }
  917.   Function IO_CloseTextFile(Var TextFile : Text) : Integer;
  918.   Var ErrCode : Integer;
  919.   Begin
  920.      ErrCode := IO_CloseText(TextFile);
  921.      If ErrCode = Ord(FileNotOpen) Then  { Really, if it was already closed why bother about it }
  922.        IO_CloseTextFile := 0
  923.      Else
  924.        IO_CloseTextFile := ErrCode;
  925.   End;
  926.  
  927.   { The File is pointed to by FullName if you can open and close it
  928.     successfully.  I recently found out that, when used to verify the
  929.     existance of an executable on a drive accessed through a new
  930.     NetWare VLM, this will frequently return False because the VLM
  931.     won't allow an executable to be opened as InOut as all non-text
  932.     files are in a BP program. }
  933.   Function IO_FileIsHere(FullName : PathStr) : Boolean;
  934.   Var ErrCode : Integer;
  935.       GenericFile : File;
  936.   Begin
  937.     ErrCode := IO_OpenFile(FullName, GenericFile, ResetFile);
  938.     If ErrCode = 0 Then
  939.       IO_FileIsHere := IO_Close(GenericFile) = 0
  940.     Else
  941.       IO_FileIsHere := False;
  942.   End;
  943.  
  944.   { This Function works for both files AND directories }
  945.   Function IO_Exists(Path : PathStr ; Attr : byte) : Boolean ;
  946.   Var DirInfo : Dos.SearchRec;
  947.       InDirStr : Dos.DirStr;
  948.       InNameStr : Dos.NameStr;
  949.       InExtStr  : Dos.ExtStr;
  950.   begin
  951.     If Attr = Dos.Directory Then
  952.       Path := IO_AddSlash(Path);
  953.     FSplit(Path, InDirStr, InNameStr, InExtStr);
  954.     InDirStr := IO_AddSlash(InDirStr);
  955.     If InNameStr = '' Then
  956.       InNameStr := '*';
  957.     If InExtStr = '' Then
  958.       InExtStr := '.*';
  959.     findfirst(InDirStr + InNameStr + InExtStr, Attr, DirInfo) ;
  960.  
  961.     IO_Exists := doserror = 0 ;
  962.  
  963.   end ; 
  964.  
  965.    { Jeffrey Watson's original Pascal Functions GetFileAttr, SetFileAttr,
  966.      GetFileTime, SetFileTime have each been broken into 2 parts: one that
  967.      does the setup and cleanup and calls the other which does everything else
  968.      in between.  This way the in between part can be accessed by code that has
  969.      already setup the file for other uses.  Also, the setup and cleanup code
  970.      is shared with other Functions in this unit. }
  971.  
  972.   { Get the file creation time for an OPEN!!! file and any error }
  973.   Function IO_GetFTime(Var GenericFile : File; Var InTime : Longint) : Integer;
  974.   Begin
  975.     { Since DosError is not traditional associated with this error, I'm
  976.       forcing it to be sure that the BP Function is called with an open file }
  977.     If FileRec(GenericFile).Mode = fmClosed Then
  978.       IO_GetFTime := 103
  979.     Else
  980.       Begin
  981.         getftime(GenericFile, InTime) ;
  982.         IO_GetFTime := Dos.DosError;
  983.       End;
  984.   End;
  985.  
  986.   { Set the file creation time for an OPEN!!! file and get any error }
  987.   Function IO_SetFTime(Var GenericFile : File; InTime : Longint) : Integer;
  988.   Begin
  989.     { Since DosError is not traditional associated with this error, I'm
  990.       forcing it to be sure that the BP Function is called with an open file }
  991.     If FileRec(GenericFile).Mode = fmClosed Then
  992.       IO_SetFTime := 103
  993.     Else
  994.       Begin
  995.         setftime(GenericFile, InTime) ;
  996.         IO_SetFTime := Dos.DosError;
  997.       End;
  998.   End;
  999.  
  1000.   { Open the file, Get its creation time, close it, and get any error }
  1001.   Function IO_GetFileTime(Const PathName : PathStr; Var Time : Longint) : Integer;
  1002.   Var GenericFile : file ;
  1003.       ErrCode : word ;
  1004.   begin
  1005.     ErrCode := 0;
  1006.     ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
  1007.     If ErrCode = 0 then
  1008.       begin
  1009.         ErrCode := IO_GetFTime(GenericFile, Time) ;
  1010.         If ErrCode = 0 Then
  1011.           ErrCode := IO_Close(GenericFile);
  1012.       end ; (* if *)
  1013.  
  1014.     IO_GetFileTime := ErrCode ;
  1015.  
  1016.   end ;
  1017.  
  1018.   { Open the file, Set its creation time, close it, and get any error }
  1019.   Function IO_SetFileTime(Const PathName : PathStr ; Time : Longint) : Integer;
  1020.   Var GenericFile : file ;
  1021.       ErrCode : Integer ;
  1022.   begin
  1023.     ErrCode := 0;
  1024.     ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
  1025.     If ErrCode = 0 then
  1026.       begin
  1027.         ErrCode := IO_SetFTime(GenericFile, Time) ;
  1028.         If ErrCode = 0 Then
  1029.           ErrCode := IO_Close(GenericFile);
  1030.       end ; (* if *)
  1031.  
  1032.      IO_SetFileTime := ErrCode ;
  1033.  
  1034.   end ; (* SetFileTime *)
  1035.  
  1036.   { It was using getfattr that caused me to write this unit and call for better
  1037.     generic I/O Functions, when I used GetFAttr but was looking for 0's in Attr
  1038.     to flag an error instead of DosError.  Well, a file that has been backed up
  1039.     and not changed since will always return a 0.  It is not nice to punish your
  1040.     users for routinely backing up their hard disk.  I won't have that problem
  1041.     using either of the 2 following Functions }
  1042.  
  1043.   { Get the Attr for GenericFile, presuming that GenericFile is Assigned but Closed }
  1044.  
  1045.   Function IO_GetFAttr(Var GenericFile : File; Var Attr : word) : Integer;
  1046.   Var ErrCode : word ;
  1047.   begin
  1048.     getfattr(GenericFile,Attr) ;
  1049.     if Dos.DosError <> 0 then
  1050.       Attr := $FFFF ;
  1051.     IO_GetFAttr := Dos.DosError;
  1052.   end ;
  1053.  
  1054.   { Get the Attr for PathName }
  1055.   Function IO_GetFileAttr(Const PathName : PathStr; Var Attr : word) : Integer;
  1056.   Var GenericFile : file ;
  1057.   begin
  1058.     assign(GenericFile,PathName) ;
  1059.     IO_GetFileAttr := IO_GetFAttr(GenericFile,Attr) ;
  1060.   end ;
  1061.  
  1062.   { Set the Attr for GenericFile, presuming that GenericFile is Assigned but Closed }
  1063.   Function IO_SetFAttr(Var GenericFile : File; Attr : word) : Integer;
  1064.   Var ErrCode : word ;
  1065.   begin
  1066.     setfattr(GenericFile, Attr) ;
  1067.     IO_SetFAttr := Dos.DosError;
  1068.   end ; (* GetFileAttr *)
  1069.  
  1070.   { Set the Attr for PathName }
  1071.   Function IO_SetFileAttr(Const PathName : PathStr ; Attr : word) : Integer ;
  1072.   Var GenericFile : file ;
  1073.   begin
  1074.     assign(GenericFile,PathName) ;
  1075.     IO_SetFileAttr := IO_SetFAttr(GenericFile,Attr) ;
  1076.   end ; (* SetFileAttr *)
  1077.  
  1078.   { The following Rename, Erase, and Kill file Functions are adaptations of
  1079.     code originally done by Jeffrey Watson }
  1080.  
  1081.   { Rename the GenericFile to the NewName, presuming that GenericFile
  1082.     is Assigned but Closed }
  1083.   Function IO_Rename(Var GenericFile : File; Const NewName : PathStr) : Integer;
  1084.   Begin
  1085. {$I-} Rename(GenericFile, NewName) ;
  1086. {$I+} IO_Rename := ioresult ;
  1087.   End;
  1088.  
  1089.   { Assign OldName to OldFile variable, Rename the file to NewName, and get any error }
  1090.   Function IO_RenameFile( Const OldName : PathStr;
  1091.                             Const NewName : PathStr) : integer ;
  1092.   Var Oldfile : file ;
  1093.   begin
  1094.     assign(Oldfile,OldName) ;
  1095.     IO_RenameFile := IO_Rename(OldFile,NewName) ;
  1096.   end ; { IO_RenameFile }
  1097.  
  1098.   { Erase GenericFile, presuming that GenericFile is Assigned but Closed }
  1099.   Function IO_Erase(Var GenericFile : File) : Integer;
  1100.   Begin
  1101. {$I-} erase(GenericFile) ;
  1102. {$I+} IO_Erase := ioresult ;
  1103.   End;
  1104.  
  1105.   { Erase TextFile, presuming that TextFile is Assigned but Closed }
  1106.   Function IO_EraseText(Var TextFile : Text) : Integer;
  1107.   Begin
  1108. {$I-} erase(TextFile) ;
  1109. {$I+} IO_EraseText := ioresult ;
  1110.   End;
  1111.  
  1112.   { Erase PathName and return any error }
  1113.   Function IO_EraseFile(Const PathName : PathStr) : integer ;
  1114.   Var GenericFile : file ;
  1115.   begin
  1116.     assign(GenericFile, PathName) ;
  1117.     IO_EraseFile := IO_Erase(GenericFile) ;
  1118.   end ;
  1119.  
  1120.   { Killing a File differs from erasing it it that we only want to make sure it
  1121.     doesn't exists afterwards.  It doesn't matter if it didn't exist before }
  1122.  
  1123.   Function IO_KillAFile(Const PathName : PathStr) : integer ;
  1124.   Var FileToKill : File;
  1125.       ErrCode : Integer;
  1126.   Begin
  1127.     ErrCode := IO_OpenFile(PathName, FileToKill, ResetFile);
  1128.     Case IO_GetErrorEnum(ErrCode) Of
  1129.       NoError : Begin
  1130.                   ErrCode := IO_Close(FileToKill);
  1131.                   If ErrCode = 0 Then
  1132.                     ErrCode := IO_Erase(FileToKill);
  1133.                 End;
  1134.       FileNotFound : ErrCode := 0; { In killing a file, we don't mind if it doesn't exist }
  1135.     End;
  1136.  
  1137.     IO_KillAFile := ErrCode;
  1138.   End;
  1139.  
  1140.   { IO_EraseFiles returns 0 if the files matching Path & FileSpec are erased }
  1141.  
  1142.   Function IO_EraseFiles(Path, FileSpec : PathStr) : integer;
  1143.   var ErrCode     : word          ;
  1144.       DirInfo     : Dos.SearchRec;
  1145.   begin
  1146.     Path := IO_AddSlash(Path);
  1147.     If FileSpec[1] = '\' Then Delete(FileSpec, 1, 1);
  1148.     findfirst(Path+FileSpec,Dos.AnyFile,DirInfo) ;
  1149.  
  1150.     ErrCode := 0 ;
  1151.     while (ErrCode = 0) and (doserror = 0) do
  1152.       begin
  1153.         { FindFirst gets directories as well so test Attr }
  1154.         If (DirInfo.Attr <> Directory) Then
  1155.           ErrCode := IO_EraseFile(Path + DirInfo.Name) ;
  1156.         findnext(DirInfo) ;
  1157.       End ; (* while-do *)
  1158.  
  1159.     IO_EraseFiles := ErrCode ;
  1160.  
  1161.   end ; (* IO_EraseFiles *)
  1162.  
  1163.   { This was the Function where the previous misuse of GetFAttr (See IO_GetFAttr above)
  1164.     got me into trouble.  It is not good if your program bombs with a runtime error every
  1165.     time it tries to remove a directory containing unchanged, normal, backed up files.
  1166.     At some point, I'll switch this to using a pseudo-stack or linked list on the heap,
  1167.     to eliminate any possiblity of stack overflow.  If anybody would like to do it for
  1168.     me, feel free to send me the code. }
  1169.  
  1170. {$S+} { Stack heavy recursive Function using 388 bytes for each LEVEL of subdirectory }
  1171.   Function IO_Remove(InPath : PathStr) : Integer;
  1172. {$S-}
  1173.   Var FAttrib  : Word;
  1174.       ThisFile : File;
  1175.       SrchRecd : SearchRec;
  1176.       InCurrDir,
  1177.       DefCurrDir : DirStr;
  1178.  
  1179.       { This cleaned up the code below nicely }
  1180.       Function IOErrorOccured(ErrCode : Integer) : Boolean;
  1181.       Begin
  1182.         If ErrCode <> 0 Then
  1183.           Begin
  1184.             IO_Remove := ErrCode;
  1185.             IOErrorOccured := True;
  1186.           End
  1187.         Else
  1188.           IOErrorOccured := False;
  1189.       End;
  1190.  
  1191.   Begin
  1192.     IO_Remove := 0;
  1193.     InPath := IO_DelSlash(InPath); {We are interested in directories as files }
  1194.     Assign(ThisFile, InPath);
  1195.     If IOErrorOccured(IO_GetFAttr(ThisFile, FAttrib)) Then
  1196.       Exit
  1197.     Else
  1198.       Begin
  1199.         If (FAttrib AND VolumeID) = 0 Then      { For Windows: faVolumeID }
  1200.           Begin
  1201.             If (FAttrib AND Directory) = 0 Then    { For Windows: faDirectory }
  1202.               Begin
  1203.                 { If InPath points to an ordinary file make it eraseable and do so }
  1204.  
  1205.                 If IOErrorOccured(IO_SetFAttr(ThisFile, Archive)) Then
  1206.                   Exit
  1207.                 Else
  1208.                   If IOErrorOccured(IO_Erase(ThisFile)) Then
  1209.                     Exit;
  1210.               End
  1211.             Else
  1212.               Begin
  1213.                 DefCurrDir := IO_GD(#0);  { get current directory of default drive }
  1214.  
  1215.                 { Check whether the removed directory is on the default drive }
  1216.                 If DefCurrDir[1] <> UpCase(InPath[1]) Then
  1217.                   Begin
  1218.                     { If it is not, get the current directory of drive of
  1219.                       the removed directory }
  1220.                     InCurrDir := IO_GD(InPath[1]);
  1221.                     If InCurrDir = '' Then
  1222.                       Begin
  1223.                         { If GD returns '', Function was given an invalid path }
  1224.                         IO_Remove := Ord(PathNotFound);
  1225.                         Exit;
  1226.                       End;
  1227.                   End
  1228.                 Else
  1229.                   InCurrDir := DefCurrDir; { InPath is on the default drive }
  1230.  
  1231.                 { Change current directory to InPath }
  1232.                 If IOErrorOccured(IO_ChDir(InPath)) Then
  1233.                   Exit;
  1234.                 { Start finding files in it }
  1235.                 FindFirst(InPath+'\*.*', Dos.AnyFile, SrchRecd);
  1236.                 While Dos.DosError = 0 Do
  1237.                   Begin
  1238.                     With SrchRecd do
  1239.                       Begin
  1240.                         If ((Attr AND Directory) = 0)  { Everything except }
  1241.                         OR (Name[1] <> '.') Then       { '.' directories }
  1242.                           Begin                        { will be removed }
  1243.                             { If a subdirectory is found, take care of it recursively }
  1244.                             If IOErrorOccured(IO_Remove(InPath+'\'+Name)) Then
  1245.                               Exit;
  1246.                           End;
  1247.                       End;
  1248.                     FindNext(SrchRecd); { continue search for files and subdirectories }
  1249.                   End;
  1250.  
  1251.                 { Change back to the previous current directory }
  1252.                 If IOErrorOccured(IO_ChDir(InCurrDir)) Then
  1253.                   Exit;
  1254.                 { So that you can remove this directory }
  1255.                 If IOErrorOccured(IO_RmDir(InPath)) Then
  1256.                   Exit;
  1257.  
  1258.                 { If InPath was not on the default drive, then change to the current
  1259.                   directory there so that Remove leaves the computer back at the very
  1260.                   same drive and directory it was at before it started }
  1261.                 If DefCurrDir[1] <> UpCase(InPath[1]) Then
  1262.                   If IOErrorOccured(IO_ChDir(DefCurrDir)) Then
  1263.                     Exit;
  1264.               End;
  1265.           End;
  1266.       End;
  1267.   End;
  1268.  
  1269.   { Flush the text file to the Dos Buffers }
  1270.  
  1271.   Function IO_FlushToDos(Var TextFile : Text) : Integer;
  1272.   Begin
  1273. {$I-} Flush(TextFile);
  1274. {$I+} IO_FlushToDos := IOResult;
  1275.   End;
  1276.  
  1277.   { Flush the text file to the Dos Buffers and then call
  1278.     IO_DosFlush to flush to the disk file }
  1279.  
  1280.   Function IO_FlushToDisk(Var TextFile : Text) : Integer;
  1281.   Var ErrCode : Integer;
  1282.   Begin
  1283.     ErrCode := IO_FlushToDos(TextFile);
  1284.  
  1285.     { Now use Neil Rubenking's Function to flush DOS buffers to disk }
  1286.     If ErrCode = 0 Then
  1287.       ErrCode := IO_DosFlush(TextFile);
  1288.     IO_FlushToDisk := ErrCode;
  1289.   End;
  1290.  
  1291.   { Get the file size for an OPEN!!! file and get any error }
  1292.   Function IO_FileSize(Var GenericFile : File; Var Size : Longint) : Integer;
  1293.   Begin
  1294. {$I-} Size := FileSize(GenericFile);
  1295. {$I+} IO_FileSize := IOResult;
  1296.   End;
  1297.  
  1298.   { If you open the file with a certain Blocksize, FileSize will return size
  1299.     as the number of those blocks in the file }
  1300.   Function IO_GetNoOfRecords(Const PathName : PathStr; Var Size : Longint; RecordSize : Word) : integer ;
  1301.  
  1302.   Var GenericFile : file ;
  1303.       ErrCode : Integer ;
  1304.   Begin
  1305.     ErrCode := 0;
  1306.     ErrCode := IO_OpenFileBlock(PathName, GenericFile, ResetFile, RecordSize);
  1307.     If ErrCode = 0 then
  1308.       Begin
  1309.         ErrCode := IO_FileSize(GenericFile, Size) ;
  1310.         If ErrCode = 0 Then
  1311.           ErrCode := IO_Close(GenericFile);
  1312.       End ; (* if *)
  1313.  
  1314.     IO_GetNoOfRecords := ErrCode ;
  1315.   End;
  1316.  
  1317.   { This was originally Jeffrey Watson's code. }
  1318.  
  1319.   { Getting FileSize through FindFirst was approximately 60% faster ( on a 50Mhz 486DX2,
  1320.     with fast hard drive and Smartdrv loaded) than opening the file as a file of 1 Byte
  1321.     records and then using IO_FileSize  }
  1322.   Function IO_GetFileSize(Const PathName : PathStr; Var FileSize : Longint) : integer ;
  1323.   Var DirInfo : Dos.SearchRec;
  1324.   Begin
  1325.     findfirst(PathName, Dos.Anyfile, DirInfo) ;
  1326.     If Dos.DosError = 0 Then
  1327.       FileSize := DirInfo.Size ;
  1328.     IO_GetFileSize := Dos.DosError;
  1329.   End;
  1330.  
  1331.   { Get the file position for an OPEN!!! file and get any error.  Unlike FileSize
  1332.     it would be unlikely for any programmer to intentionally try to find the
  1333.     FilePos on a closed file }
  1334.   Function IO_FilePos(Var GenericFile : File; Var FPos : Longint) : Integer;
  1335.   Begin
  1336. {$I-} FPos := FilePos(GenericFile);
  1337. {$I+} IO_FilePos := IOResult;
  1338.   End;
  1339.  
  1340.   { Seek a file position for an OPEN!!! file and get any error.}
  1341.   Function IO_FileSeek(Var GenericFile : File; FSeek : Longint) : Integer;
  1342.   Begin
  1343. {$I-} Seek(GenericFile, FSeek);
  1344. {$I+} IO_FileSeek := IOResult;
  1345.   End;
  1346.  
  1347.   { Open file PathName, and then seek a file position and get any error.}
  1348.   Function IO_GoFileSeek( Const PathName : PathStr;
  1349.                             Var GenericFile : File;
  1350.                             FSeek : Longint) : integer ;
  1351.   Var ErrCode : Integer ;
  1352.   Begin
  1353.     ErrCode := 0;
  1354.     ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
  1355.     If ErrCode = 0 then
  1356.       ErrCode := IO_FileSeek(GenericFile, FSeek) ;
  1357.  
  1358.     IO_GoFileSeek := ErrCode ;
  1359.   End;
  1360.  
  1361.   { Read a block of bytes from an OPEN!!! file and get any error.}
  1362.   Function IO_BlockRead( Var GenericFile : File;
  1363.                            Var Buffer;
  1364.                                Count     : Word;
  1365.                            Var BytesRead : Word ) : Integer;
  1366.   Begin
  1367. {$I-} BlockRead(GenericFile, Buffer, Count, BytesRead);
  1368. {$I+} IO_BlockRead := IOResult;
  1369.   End;
  1370.  
  1371.   { Open file PathName, create a buffer, read the file into the buffer, then
  1372.     close the file.  Will not work with Files of size > 64K-8 (largest buffer).
  1373.     User of Function responsible for knowing structure of file to correctly
  1374.     use bytes read into buffer.  User also responsible for using a
  1375.     FreeMem(BuffPtr, FSize) after use to remove buffer from Heap.}
  1376.   Function IO_BlockReadIntoHeap( Const PathName : PathStr;
  1377.                                    Var BuffPtr : Pointer;
  1378.                                    Var FSize : Longint ) : Integer;
  1379.   Var ErrCode : Integer;
  1380.       BytesRead : Word;
  1381.       GenericFile : File;
  1382.   Begin
  1383.     ErrCode := 0;
  1384.     ErrCode := IO_OpenFileBlock1(PathName, GenericFile, ResetFile);
  1385.     If ErrCode = 0 Then
  1386.       Begin
  1387.         ErrCode := IO_FileSize(GenericFile, FSize);
  1388.         If ErrCode = 0 Then
  1389.           Begin
  1390.             BytesRead := MaxAvail;
  1391.             If BytesRead > 65528 Then
  1392.               BytesRead := 65528;
  1393.             IF FSize > BytesRead Then
  1394.               ErrCode := Ord(InsufficientMemory)
  1395.             Else
  1396.               Begin
  1397.                 GetMem(BuffPtr, FSize);
  1398.                 { This really only works if LocHeapFunc is set to 1 }
  1399.                 If Not Assigned(BuffPtr) Then
  1400.                   ErrCode := Ord(InsufficientMemory)
  1401.                 Else
  1402.                   Begin
  1403.                     ErrCode := IO_BlockRead(GenericFile, BuffPtr, FSize, BytesRead);
  1404.                     If ErrCode = 0 Then
  1405.                       If BytesRead <> FSize Then
  1406.                         ErrCode := Ord(DiskReadError);
  1407.                   End;
  1408.               End;
  1409.             If ErrCode in [Ord(NoError), Ord(InsufficientMemory)] Then
  1410.               ErrCode := IO_CloseFile(GenericFile);
  1411.           End;
  1412.       End;
  1413.     IO_BlockReadIntoHeap := ErrCode;
  1414.   End;
  1415.  
  1416.   { Write a block of bytes to an OPEN!!! file and get any error.}
  1417.   Function IO_BlockWrite( Var GenericFile : File;
  1418.                             Var Buffer;
  1419.                             Count : Word;
  1420.                             Var Result : Word) : Integer;
  1421.   Begin
  1422. {$I-} BlockWrite(GenericFile, Buffer, Count, Result);
  1423. {$I+} IO_BlockWrite := IOResult;
  1424.   End;
  1425.  
  1426.   { Open file PathName, get pointer to a buffer, write the buffer to the file,
  1427.     then close the file.  Will not work with Buffers of size > 64K. }
  1428.   Function IO_BlockWriteFromHeap(Const PathName : PathStr;
  1429.                                          BuffPtr : Pointer;
  1430.                                          FSize : Word ) : Integer;
  1431.   Var ErrCode : Integer;
  1432.       BytesWritten : Word;
  1433.       GenericFile : File;
  1434.   Begin
  1435.     ErrCode := 0;
  1436.     If Not Assigned(BuffPtr) Then
  1437.       ErrCode := 204  { Invalid Pointer Operation }
  1438.     Else
  1439.       Begin
  1440.         If FSize > 65535 Then
  1441.           ErrCode := Ord(DiskWriteError)
  1442.         Else
  1443.           Begin
  1444.             ErrCode := IO_OpenFileBlock1(PathName, GenericFile, RewriteFile);
  1445.             If ErrCode = 0 Then
  1446.               Begin
  1447.                 ErrCode := IO_BlockWrite(GenericFile, BuffPtr, FSize, BytesWritten);
  1448.                 If ErrCode = 0 Then
  1449.                   If BytesWritten <> FSize Then
  1450.                     ErrCode := Ord(DiskWriteError);
  1451.               End;
  1452.           End;
  1453.       End;
  1454.     If ErrCode = 0 Then
  1455.       ErrCode := IO_CloseFile(GenericFile);
  1456.     IO_BlockWriteFromHeap := ErrCode;
  1457.   End;
  1458.  
  1459.   { EO Functions are Boolean because they are Boolean to start with and are
  1460.     often embedded in While..Do and Repeat..Until loops.  Since an error
  1461.     forces them to return False, the ErrCode can be examined afterward to
  1462.     determine whether it was a normal or abnormal End }
  1463.  
  1464.   Function IO_EOF(Var GenericFile : File; Var ErrCode : Integer) : Boolean;
  1465.   Begin
  1466. {$I-} IO_EOF := EOF(GenericFile);
  1467. {$I+} ErrCode := IOResult;
  1468.       If ErrCode <> 0 Then
  1469.         IO_EOF := False;
  1470.   End;
  1471.  
  1472.   Function IO_EOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  1473.   Begin
  1474. {$I-} IO_EOLn := EOLn(TextFile);
  1475. {$I+} ErrCode := IOResult;
  1476.       If ErrCode <> 0 Then
  1477.         IO_EOLn := False;
  1478.   End;
  1479.  
  1480.   Function IO_SeekEOF(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  1481.   Begin
  1482. {$I-} IO_SeekEOF := SeekEOF(TextFile);
  1483. {$I+} ErrCode := IOResult;
  1484.       If ErrCode <> 0 Then
  1485.         IO_SeekEOF := False;
  1486.   End;
  1487.  
  1488.   Function IO_SeekEOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
  1489.   Begin
  1490. {$I-} IO_SeekEOLn := SeekEOLn(TextFile);
  1491. {$I+} ErrCode := IOResult;
  1492.       If ErrCode <> 0 Then
  1493.         IO_SeekEOLn := False;
  1494.   End;
  1495.  
  1496.   { compares 2 equal size areas of memory byte for byte, will only be true if
  1497.     they are IDENTICAL in the interval [Index..Index+Size].  Written originally
  1498.     to compare 2 equal type records which the compiler should be able to do
  1499.     since it can use REP MOVSB to copy a record to another of the same type.
  1500.     Index = 0 will start at the first byte.  In simular fashion to the FillChar
  1501.     and Move Procedures, the User is responsible for making sure that Size
  1502.     accounts for starting place Index }
  1503.  
  1504.   { Jeffrey Watson's original Pascal Function written before 6.0 using a while loop
  1505.     and array indexing has been translated to BASM using a ASM string CMPSB because
  1506.     doing so is much smaller and faster. }
  1507.  
  1508.   Function IO_Equals(Var X, Y; Index : word; Size : word) : Boolean; assembler;
  1509.   asm
  1510.     mov dx, ds
  1511.     lds si, X               { Get X Address }
  1512.     les di, Y               { Get Y Address }
  1513.     add si, word ptr Index
  1514.     add di, word ptr Index  { Move Index bytes over for start of compare }
  1515.     mov cx, Size            { Compare Size Bytes }
  1516.     mov bl, 1               { Preset return for True }
  1517.     cld                     { make compare go forward }
  1518.     repe cmpsb              { run through comparison }
  1519.     je @1                   { if Zero flag still zero, than successful completion }
  1520.     mov bl, 0               { else we stopped when two bytes found different }
  1521. @1: mov al, bl              { set the return }
  1522.     mov ds, dx
  1523.   end;
  1524.  
  1525. { following are methods for objects to do some commonly done file processes }
  1526.  
  1527. { This object will find files and then let the user deal with them it a
  1528.   DoFileOperation method that overrides the abstract one here.  Using Abstract
  1529.   in the one here will force that. }
  1530.  
  1531. { If not even one file matching Path can be found the object will fail }
  1532. Constructor TFindFileObj.InitAndFindFirst(Const Path : PathStr; Attr: Word);
  1533. Begin
  1534.   FindFirst(Path, Attr, FSearch);
  1535.   If DosError <> 0 Then
  1536.     Fail
  1537.   Else
  1538.     Begin
  1539.       FFError := DosError;
  1540.       ParseFSearch;
  1541.     End;
  1542. End;
  1543.  
  1544. Destructor TFindFileObj.EndFindFile;
  1545. Begin
  1546. End;
  1547.  
  1548. { Parse and store everything about the file for easy access by methods }
  1549. Procedure TFindFileObj.ParseFSearch;
  1550. Var DT : DateTime;
  1551.     PrdPos : word;
  1552. Begin
  1553.   With FSearch Do
  1554.     Begin
  1555.       FAttr := Attr;
  1556.       FTime := Time;
  1557.       FSize := Size;
  1558.       FNameExt := Name;
  1559.     End;
  1560.   UnpackTime(FTime, DT);
  1561.   With DT do
  1562.     Begin
  1563.       FYear  := Year;
  1564.       FMonth := Month;
  1565.       FDay   := Day;
  1566.       FHour  := Hour;
  1567.       FMin   := Min;
  1568.       FSec   := Sec;
  1569.     End;
  1570.   PrdPos := Pos('.',FNameExt);
  1571.   If PrdPos <= 0 then
  1572.     PrdPos := Succ(Length(FNameExt));
  1573.   FName := Copy(FNameExt, 1, Pred(PrdPos));
  1574.   FExt := Copy(FNameExt, Succ(PrdPos), 3);
  1575. End;
  1576.  
  1577. { Find the next file }
  1578. Procedure TFindFileObj.DoFindNext;
  1579. Begin
  1580.   FindNext(FSearch);
  1581.   ParseFSearch;
  1582.   FFError := DosError;
  1583. End;
  1584.  
  1585. { Here's the loop that one usually has to create out of whole cloth each time
  1586.   files have to be found.  Using FileOpError allows the user to pass any errors
  1587.   up to the program from the DoFileOperation method }
  1588. Function TFindFileObj.DoFindFileLoop : Integer;
  1589. Var FileOpError : Integer;
  1590. Begin
  1591.   FileOpError := 0;
  1592.   While (FFError = 0) and (FileOpError = 0) do
  1593.     Begin
  1594.       FileOpError := DoFileOperation;
  1595.       DoFindNext;
  1596.     End;
  1597.   DoFindFileLoop := FileOpError;
  1598. End;
  1599.  
  1600. { OVERRIDE this method }
  1601. Function TFindFileObj.DoFileOperation : Integer;
  1602. Begin
  1603.   DoFileOperation := 0;
  1604.   Abstract;
  1605. End;
  1606.  
  1607. { TCopyFileObj copies Source to Dest a CopyBuffer at a time,
  1608.   using BlockRead and BlockWrite }
  1609.  
  1610. Constructor TCopyFileObj.InitCopy(Const Source : PathStr; Const Dest : PathStr);
  1611. Var TempDir : DirStr;
  1612.     TempName : NameStr;
  1613.     TempExt  : ExtStr;
  1614. Begin
  1615.   CopyBuffer := Nil;
  1616.   BuffSize := MaxAvail;
  1617.   If BuffSize > 65528 Then
  1618.     BuffSize := 65528;
  1619.   GetMem(CopyBuffer, BuffSize);
  1620.  
  1621.   { This next would only work if LocHeapFunc = 1.  And CopyBuffer shouldn't
  1622.     be unassigned from lack of memory, but it may be so for other reasons}
  1623.   If Not Assigned(CopyBuffer) Then
  1624.     Fail;
  1625.  
  1626.   { Paths are stored full and in parts so that a descendent object below
  1627.     that copies sets of files in a loop can replace them }
  1628.   FSplit(Source, TempDir, TempName, TempExt);
  1629.   SourceFullPath := Source;
  1630.   SourcePath := TempDir;
  1631.   SourceName := TempName + TempExt;
  1632.  
  1633.   FSplit(Dest, TempDir, TempName, TempExt);
  1634.   DestFullPath := Dest;
  1635.   DestPath := TempDir;
  1636.   DestName := TempName + TempExt;
  1637. End;
  1638.  
  1639. { If CopyBuffer was successfully created, get rid of it here }
  1640. Destructor TCopyFileObj.EndCopy;
  1641. Begin
  1642.   If Assigned(CopyBuffer) Then
  1643.     FreeMem(CopyBuffer, BuffSize);
  1644. End ;
  1645.  
  1646. { Set the Source and Dest to the same name }
  1647. Procedure TCopyFileObj.SetNames(Const FileName : Strg12) ;
  1648. Begin
  1649.   SourceName := FileName;
  1650.   DestName   := FileName;
  1651. End;
  1652.  
  1653. { Set the paths }
  1654. Procedure TCopyFileObj.SetPaths(Const SPath : PathStr; Const DPath : PathStr);
  1655. Begin
  1656.   SourcePath := SPath;
  1657.   DestPath   := DPath;
  1658. End;
  1659.  
  1660. Function TCopyFileObj.GetErr : Integer;
  1661. Begin
  1662.   GetErr := Err;
  1663. End;
  1664.  
  1665. { Open the files with Block Size of 1 Byte }
  1666. Function TCopyFileObj.OpenFiles : Integer;
  1667. Begin
  1668.   Err := IO_OpenFileBlock1( SourcePath+SourceName, SourceFile, ResetFile);
  1669.   If Err = 0 Then
  1670.     Err := IO_OpenFileBlock1( DestPath+DestName, DestFile, RewriteFile);
  1671.   OpenFiles := Err;
  1672. End;
  1673.  
  1674. Function TCopyFileObj.CloseFiles : Integer;
  1675. Begin
  1676.   Err := IO_CloseFile(SourceFile);
  1677.   If Err = 0 Then
  1678.     Err := IO_CloseFile(DestFile);
  1679.   CloseFiles := Err;
  1680. End;
  1681.  
  1682. { Copy from Source to Dest, a CopyBuffer at a time }
  1683. Function TCopyFileObj.DoFileCopy : Integer;
  1684. Var BytesRead, BytesWritten : Word;
  1685. Begin
  1686.   If OpenFiles = 0 Then
  1687.     Begin
  1688.       repeat
  1689.         Err := IO_Blockread(SourceFile, CopyBuffer^, BuffSize, BytesRead) ;
  1690.         if Err = 0 then
  1691.           Err := IO_BlockWrite(DestFile, CopyBuffer^, BytesRead, BytesWritten) ;
  1692.       until (Err <> 0) or IO_Eof(SourceFile, Err)
  1693.          or (BytesRead <> BuffSize) or (BytesRead <> BytesWritten);
  1694.       If Err = 0 Then
  1695.         Err := CloseFiles;
  1696.     End;
  1697.   DoFileCopy := Err ;
  1698. end ;
  1699.  
  1700. Function TCopyFileObj.DoCopies : Integer;
  1701. Var SrchRecd : Dos.SearchRec;
  1702. Begin
  1703.   FindFirst(SourceFullPath, Dos.AnyFile, SrchRecd);
  1704.   While (DosError = 0) and (Err = 0) do
  1705.     Begin
  1706.       If SrchRecd.Attr <> dos.directory Then
  1707.         Begin
  1708.           SetNames(SrchRecd.Name);
  1709.           Err := DoFileCopy;
  1710.         End;
  1711.       If Err = 0 Then
  1712.         FindNext(SrchRecd);
  1713.     End;
  1714.   DoCopies := Err;
  1715. End;
  1716.  
  1717. { Move is identical to Copy except that Source is erased afterwards }
  1718. Constructor TMoveFileObj.InitMove(Source, Dest : PathStr);
  1719. Begin
  1720.   Inherited InitCopy(Source, Dest);
  1721. End;
  1722.  
  1723. Destructor TMoveFileObj.EndMove;
  1724. Begin
  1725.   Inherited EndCopy;
  1726. End;
  1727.  
  1728. Function TMoveFileObj.DoFileCopy : Integer;
  1729. Begin
  1730.   Err := Inherited DoFileCopy;
  1731.   If Err = 0 Then
  1732.     Err := IO_EraseFile(SourcePath+SourceName);
  1733. End;
  1734.  
  1735. End.
  1736.